# encoding=utf-8
import argparse
import hashlib
import os
import tempfile
import time
import logging
import subprocess
import fastapi
import uvicorn
from asyncio import Lock
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from logging.handlers import TimedRotatingFileHandler


class AppConfig:
    # 常规配置
    CONFIG_PATH = os.path.join(os.path.dirname(__file__))
    CACHE_PATH = os.path.join(CONFIG_PATH, "runtime-cache")
    LOGGER_PATH = os.path.join(CONFIG_PATH, "runtime-log")


def set_logger():
    logger = logging.getLogger()  # root
    logger.setLevel(logging.INFO)

    file_handler = TimedRotatingFileHandler(
        filename=os.path.join(AppConfig.LOGGER_PATH, "app-log"),
        when='D', interval=1
    )
    file_handler.suffix = "%Y-%m-%d.log"

    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)

    logging_format = logging.Formatter(
        "%(asctime)s - %(filename)s - %(funcName)s - %(lineno)d - %(levelname)s - %(message)s")

    file_handler.setFormatter(logging_format)
    console_handler.setFormatter(logging_format)

    logger.addHandler(file_handler)
    logger.addHandler(console_handler)


def generate_time_mills():
    return int(round(time.time() * 1000))


def generate_task_id(name: str):
    str_data = name + str(generate_time_mills())
    encoded = hashlib.sha256(str_data.encode()).hexdigest()
    return encoded


app = FastAPI()
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=False,
    allow_methods=["*"],
    allow_headers=["*"]
)

async_lock = Lock()

# CMD ["python3", "/workspace/OCTOptovueDataConvertTools.py", "--workdir=/workspace/workdir"]
command_run = "echo {} | sudo -S {}"
command_convert = "python3 {} --workdir={}"


class OptovueFiveController:
    @staticmethod
    @app.post("/run/optovue/five")
    async def run(file: fastapi.UploadFile):
        # save to tmp, use PIL to decode
        temp_dir_name = generate_task_id("hello")
        abs_workdir = os.path.join(AppConfig.CACHE_PATH, temp_dir_name)
        abs_user_path = os.path.join(abs_workdir, "workdir", "user_input")
        os.makedirs(abs_user_path, exist_ok=True)

        logging.info("save user path: {}".format(abs_user_path))

        temp_file = os.path.basename(tempfile.NamedTemporaryFile(suffix=".png").name)
        abs_file_path = os.path.join(abs_user_path, temp_file)
        with open(abs_file_path, 'wb') as abs_file:
            abs_file.write(await file.read())

        logging.info("write done file: {}".format(abs_file_path))

        # run infer
        async with async_lock:
            start_time = time.perf_counter()
            s, r = subprocess.getstatusoutput(command_convert.format(run_path, os.path.join(abs_workdir, "workdir")))
            end_time = time.perf_counter()
            logging.info("infer cost: {}s".format(end_time - start_time))
            if s != 0:
                logging.error(r)
                return JSONResponse(
                    status_code=fastapi.status.HTTP_500_INTERNAL_SERVER_ERROR,
                    content={
                        'msg': r
                    }
                )
            else:
                logging.info(r)

        # return
        dst_name = os.path.join(abs_workdir, "workdir", "out_raw_data", temp_file)
        return fastapi.responses.FileResponse(dst_name)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("--path", type=str)
    parser.add_argument("--password", type=str, default="")
    args = parser.parse_args()
    if args.path is None:
        logging.error("run path is empty")
        exit(-1)

    run_path = args.path
    sudo_password = args.password

    os.makedirs(AppConfig.CACHE_PATH, exist_ok=True)
    os.makedirs(AppConfig.LOGGER_PATH, exist_ok=True)
    set_logger()

    uvicorn.run(app=app, host="0.0.0.0", port=30001)
